Tiled & woven thematic maps

David O’Sullivan & Luke R Bergmann

A preview

The TL;DR;

Where we are headed

Two new kinds of thematic map for handling complex multivariate data associated with polygons.

We’ve developed python code that helps you make tiled and woven maps.

Tiling theory

Mosaic world map by Chris Chamberlain; see this article and this video for more.

A plane tiling is…

“… a countable family of closed sets \(\mathcal{T}=\{T_1,T_2,\ldots\}\) which covers the plane without gaps or overlaps. More explicitly, the union of the sets \(T_1,T_2,\ldots\) (which are known as the tiles of \(\mathcal{T}\)) is to be the whole plane, and the interiors of the sets \(T_i\) are to be pairwise disjoint” (Grünbaum and Shephard 1987, page 16)

… a GIS coverage!

Symmetry in mathematical tiling

The symmetries of a tiling map tiles on to other tiles.

The sets of tiles related to one another in this way are the transitivity groups of the tiling.

An isohedral tiling has only one transitivity group.

Symmetry in cartographic tiling

Directionality or orientation is irrelevant to mathematicians because they consider tiles and tilings identical under rotation.

However, they are important for mapping. Many isohedral tilings contain directionally distinguishable tiles.

We likely need a new cartographic notion of the transitivity groups where different tile orientations ‘count’ as different.

What we learned from tiling theory

¯\_(ツ)_/¯

We are not entirely sure… yet!

Tile units

The building blocks of our approach.

The basic pattern

cairo_tile = TileUnit(tiling_type = "cairo")
cairo_tile.plot(r = 1, show_vectors = True, 
                figsize = (7, 7)).set_axis_off()

Triangles, squares, hexagons

triangles = TileUnit(tile_shape = TileShape.TRIANGLE)
squares = TileUnit() 
hexagons = TileUnit(tile_shape = TileShape.HEXAGON)

Archimedean tilings by regular polygons

Laves (the Archimedean duals)

Hexagon dissections and colourings

In general

These (and more) are all available with the same command:

tile = TileUnit(
  tiling_type = "cairo|archimedean|laves|hex-dissection|square-dissection",
  n = ..., code = "3.3.4.3.4", dissection_offset = 0|1,  # optional
  spacing = 500, crs = 2193
)

We can then make various adjustments:

tile = tile.transform_rotate(...)
tile = tile.transform_scale(...)
tile = tile.transform_skew(...)
tile = tile.inset_tile(...)
tile = tile.inset_elements(...)
tile = tile.scale_elements(...)

‘Insetting’

Insetting relative to the repeating tile unit helps distinguish elements.

Weaving

For a lot more on woven maps, see our earlier talk.

Some weaving theory

Weaving theory leads to useful matrix-based approaches.

Primarily for biaxial weaves.

We (eventually) figured out how to extend the matrix approach to triaxial weaves (see our notes).

Weave units

We make these the same way we make tile units.

w1 = WeaveUnit(weave_type = "twill", n = 3, aspect = .9, 
               strands = "ab-|cde-")

w2 = WeaveUnit(weave_type = "cube", aspect = .8, 
               strands = "a-c|d-f|g-i")

The strands parameter specifies which strands in each axis (separated by the "|") are distinct, and also allows us to ‘skip’ strands on "-" characters .

Making a map

Vermeer’s The Geographer (∽1675) from commons.wikimedia.org.

Tiling a dataset

Making a tiled map, TileUnits and WeaveUnits are both Tileable objects and are treated the same.

We make a Tiling from one of these along with a polygon dataset to be tiled.

tiling = Tiling(cairo_tile, region, id_var = "DZ2018")

The tiling process

The Tiling generates a TileGrid which controls copying and translating the tileable units into a tiled or woven map.

Making a tiled map

Next, make a TiledMap by calling the get_tiled_map() method of the Tiling.

tiled_map = tiling.get_tiled_map(prioritise_tiles = True)

This controls combining the Tiling with the geospatial data by overlay and dissolve operations.

Here we can choose to emphasize tile boundaries, or zone boundaries in the dataset using the prioritise_tiles setting.

Designing the final map

Here we have to specify the mapping from tileable element identifiers ("a", "b", "c" …) to data variables.

tiled_map.variables = dict(a = "Rank_Emplo", b = "Rank_Incom", 
                           c = "Rank_Crime", d = "Rank_Housi")

We also specify the mapping from variable names to chosen colour palettes.

tiled_map.colourmaps = dict(zip(tiled_map.variables.values(),
                                ("Reds", "Blues", "Greens", "Greys")))

Now we can render a map…

fig = tiled_map.render(use_ellipse = True, figsize = (12, 6),
                       legend_dx = -0.05, legend_dy = 0.05)

There are a range of options here particularly in relation to the tricky business of generating a legend.

Some more examples

Because we can…

Further work

Image from publicdomainpictures.net by Andrea Stöckel

This work is an exploration of pattern, orientation, texture, and colour as visual variables. We invite collaboration!

The code used to make this talk is available at github.com/DOSull/weaving-space.

We’re close to finalizing a release, and are keen to get feedback, contributions, and ideas, especially:

  • Actual use of the approach
  • QGIS plugin development

If you have a project that might benefit from our approach, please reach out!

Questions?

github.com/DOSull/weaving-space